#include <linux/types.h>
#include <linux/rbtree.h>
#include <linux/sched.h>
#include <linux/kernel.h>

// static const struct sched_class fair_sched_class;

static unsigned int sched_nr_latency = 5;
unsigned int sysctl_sched_latency = 5000000ULL;
unsigned int sysctl_sched_min_granularity = 1000000ULL;
unsigned int sysctl_sched_migration_cost = 500000UL;

static const struct sched_class fair_sched_class;

#define entity_is_task(se)	1

#define for_each_sched_entity(se) \
		for (; se; se = NULL)

static inline struct task_struct *task_of(struct sched_entity *se)
{
	return container_of(se, struct task_struct, se);
}

static inline struct cfs_rq *cfs_rq_of(struct sched_entity *se)
{
	struct task_struct *p = task_of(se);
	struct rq *rq = task_rq(p);

	return &rq->cfs;
}

static inline struct cfs_rq *task_cfs_rq(struct task_struct *p)
{
	return &task_rq(p)->cfs;
}

static inline struct cfs_rq *group_cfs_rq(struct sched_entity *grp)
{
	return NULL;
}

static inline void update_load_add(struct load_weight *lw, unsigned long inc)
{
	lw->weight += inc;
	lw->inv_weight = 0;
}

static inline void update_load_sub(struct load_weight *lw, unsigned long dec)
{
	lw->weight -= dec;
	lw->inv_weight = 0;
}

static const int prio_to_weight[40] = {
 /* -20 */     88761,     71755,     56483,     46273,     36291,
 /* -15 */     29154,     23254,     18705,     14949,     11916,
 /* -10 */      9548,      7620,      6100,      4904,      3906,
 /*  -5 */      3121,      2501,      1991,      1586,      1277,
 /*   0 */      1024,       820,       655,       526,       423,
 /*   5 */       335,       272,       215,       172,       137,
 /*  10 */       110,        87,        70,        56,        45,
 /*  15 */        36,        29,        23,        18,        15,
};

static const u32 prio_to_wmult[40] = {
 /* -20 */     48388,     59856,     76040,     92818,    118348,
 /* -15 */    147320,    184698,    229616,    287308,    360437,
 /* -10 */    449829,    563644,    704093,    875809,   1099582,
 /*  -5 */   1376151,   1717300,   2157191,   2708050,   3363326,
 /*   0 */   4194304,   5237765,   6557202,   8165337,  10153587,
 /*   5 */  12820798,  15790321,  19976592,  24970740,  31350126,
 /*  10 */  39045157,  49367440,  61356676,  76695844,  95443717,
 /*  15 */ 119304647, 148102320, 186737708, 238609294, 286331153,
};

static inline struct sched_entity *parent_entity(struct sched_entity *se)
{
	return NULL;
}

static inline void inc_cpu_load(struct rq *rq, unsigned long load)
{
	update_load_add(&rq->load, load);
}

static inline void dec_cpu_load(struct rq *rq, unsigned long load)
{
	update_load_sub(&rq->load, load);
}

static struct sched_entity *__pick_next_entity(struct cfs_rq *cfs_rq)
{
	struct rb_node *left = cfs_rq->rb_leftmost;

	// printf("this is %s(): %d\r\n", __func__, __LINE__);

	if (!left)
		return NULL;

	// printf("this is %s(): %d\r\n", __func__, __LINE__);

	return rb_entry(left, struct sched_entity, run_node);
}

static void __clear_buddies(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
	if (!se || cfs_rq->last == se)
		cfs_rq->last = NULL;

	if (!se || cfs_rq->next == se)
		cfs_rq->next = NULL;
}

static void clear_buddies(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
	for_each_sched_entity(se)
		__clear_buddies(cfs_rq_of(se), se);
}

static struct sched_entity *pick_next_entity(struct cfs_rq *cfs_rq)
{
	struct sched_entity *se = __pick_next_entity(cfs_rq);
	struct sched_entity *left = se;

	// // if (cfs_rq->next && wakeup_preempt_entity(cfs_rq->next, left) < 1)
	// if (cfs_rq->next)
	// 	se = cfs_rq->next;

	// /*
	//  * Prefer last buddy, try to return the CPU to a preempted task.
	//  */
	// if (cfs_rq->last)
	// 	se = cfs_rq->last;

	// clear_buddies(cfs_rq, se);

	return se;
}

static inline struct rq *rq_of(struct cfs_rq *cfs_rq)
{
	return container_of(cfs_rq, struct rq, cfs);
}

static inline void update_stats_curr_start(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
	/*
	 * We are starting a new run period:
	 */
	se->exec_start = rq_of(cfs_rq)->clock;
}

static void __dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
	if (cfs_rq->rb_leftmost == &se->run_node) {
		struct rb_node *next_node;

		next_node = rb_next(&se->run_node);
		cfs_rq->rb_leftmost = next_node;
		// printf("this is %s(): %d\r\n", __func__, __LINE__);
	}

	rb_erase(&se->run_node, &cfs_rq->tasks_timeline);
}

static void set_next_entity(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
	/* 'current' is not kept within the tree. */
	if (se->on_rq) {
		/*
		 * Any task has to be enqueued before it get to execute on
		 * a CPU. So account for the time it spent waiting on the
		 * runqueue.
		 */
		// update_stats_wait_end(cfs_rq, se);
		// printf("this is %s(): %d\r\n", __func__, __LINE__);
		__dequeue_entity(cfs_rq, se);
	}

	update_stats_curr_start(cfs_rq, se);
	cfs_rq->curr = se;

	se->prev_sum_exec_runtime = se->sum_exec_runtime;
}

static struct task_struct *pick_next_task_fair(struct rq *rq)
{
	struct task_struct *p;
	struct cfs_rq *cfs_rq = &rq->cfs;
	struct sched_entity *se;

	// printf("this is %s(): %d\r\n", __func__, __LINE__);

	if (!cfs_rq->nr_running)
		return NULL;

	// printf("this is %s(): %d\r\n", __func__, __LINE__);

	do {
		se = pick_next_entity(cfs_rq);
		set_next_entity(cfs_rq, se);
		cfs_rq = group_cfs_rq(se);
	} while (cfs_rq);

	p = task_of(se);

	return p;
}

static inline u64 max_vruntime(u64 min_vruntime, u64 vruntime)
{
	s64 delta = (s64)(vruntime - min_vruntime);
	if (delta > 0)
		min_vruntime = vruntime;

	return min_vruntime;
}

static inline u64 min_vruntime(u64 min_vruntime, u64 vruntime)
{
	s64 delta = (s64)(vruntime - min_vruntime);
	if (delta < 0)
		min_vruntime = vruntime;

	return min_vruntime;
}

static void update_min_vruntime(struct cfs_rq *cfs_rq)
{
	u64 vruntime = cfs_rq->min_vruntime;

	if (cfs_rq->curr)
		vruntime = cfs_rq->curr->vruntime;

	if (cfs_rq->rb_leftmost) {
		struct sched_entity *se = rb_entry(cfs_rq->rb_leftmost,
						   struct sched_entity,
						   run_node);

		if (!cfs_rq->curr)
			vruntime = se->vruntime;
		else
			vruntime = min_vruntime(vruntime, se->vruntime);
	}

	cfs_rq->min_vruntime = max_vruntime(cfs_rq->min_vruntime, vruntime);
}

# define WMULT_CONST	(~0UL)
#define WMULT_SHIFT	32
#define SRR(x, y) (((x) + (1UL << ((y) - 1))) >> (y))
#define LONG_MAX	((long)(~0UL>>1))

static unsigned long calc_delta_mine(unsigned long delta_exec, unsigned long weight,
		struct load_weight *lw)
{
	u64 tmp;

	if (!lw->inv_weight) {
		if (BITS_PER_LONG > 32 && lw->weight >= WMULT_CONST)
			lw->inv_weight = 1;
		else
			lw->inv_weight = 1 + (WMULT_CONST-lw->weight/2)
				/ (lw->weight+1);
	}

	tmp = (u64)delta_exec * weight;
	/*
	 * Check whether we'd overflow the 64-bit multiplication:
	 */
	if (tmp > WMULT_CONST)
		tmp = SRR(SRR(tmp, WMULT_SHIFT/2) * lw->inv_weight,
			WMULT_SHIFT/2);
	else
		tmp = SRR(tmp * lw->inv_weight, WMULT_SHIFT);

	return (unsigned long)min(tmp, (u64)(unsigned long)LONG_MAX);
}

#define SCHED_LOAD_SHIFT	10
#define SCHED_LOAD_SCALE	(1L << SCHED_LOAD_SHIFT)
#define NICE_0_LOAD		SCHED_LOAD_SCALE

static inline unsigned long calc_delta_fair(unsigned long delta, struct sched_entity *se)
{
	if (se->load.weight != NICE_0_LOAD)
		delta = calc_delta_mine(delta, NICE_0_LOAD, &se->load);

	return delta;
}

static inline void __update_curr(struct cfs_rq *cfs_rq, struct sched_entity *curr,
	      unsigned long delta_exec)
{
	unsigned long delta_exec_weighted;

	// printf("this is %s(): %d\r\n", __func__, __LINE__);

	// schedstat_set(curr->exec_max, max((u64)delta_exec, curr->exec_max));

	curr->sum_exec_runtime += delta_exec;
	// schedstat_add(cfs_rq, exec_clock, delta_exec);
	delta_exec_weighted = calc_delta_fair(delta_exec, curr);
	curr->vruntime += delta_exec_weighted;
	update_min_vruntime(cfs_rq);
}

static void update_curr(struct cfs_rq *cfs_rq)
{
	struct sched_entity *curr = cfs_rq->curr;
	u64 now = rq_of(cfs_rq)->clock;
	unsigned long delta_exec;

	// printf("this is %s(): %d\r\n", __func__, __LINE__);

	if (!curr)
		return;

	/*
	 * Get the amount of time the current task was running
	 * since the last time we changed load (this cannot
	 * overflow on 32 bits):
	 */
	delta_exec = (unsigned long)(now - curr->exec_start);
	// printf("this is %s(): %d   now is %ld, curr->exec_start is %ld\r\n", __func__, __LINE__, now, curr->exec_start);
	if (!delta_exec)
		return;
	// printf("this is %s(): %d\r\n", __func__, __LINE__);

	__update_curr(cfs_rq, curr, delta_exec);
	curr->exec_start = now;

	// if (entity_is_task(curr)) {
	// 	struct task_struct *curtask = task_of(curr);

	// 	trace_sched_stat_runtime(curtask, delta_exec, curr->vruntime);
	// 	cpuacct_charge(curtask, delta_exec);
	// 	account_group_exec_runtime(curtask, delta_exec);
	// }
}

static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int wake_flags)
{
	struct task_struct *curr = rq->curr;
	struct sched_entity *se = &curr->se, *pse = &p->se;
	struct cfs_rq *cfs_rq = task_cfs_rq(curr);
	// int sync = wake_flags & WF_SYNC;
	int scale = cfs_rq->nr_running >= sched_nr_latency;

	update_curr(cfs_rq);

	if (rt_prio(p->prio)) {
		resched_task(curr);
		return;
	}

	if (p->sched_class != &fair_sched_class)
		return;

	if (se == pse)
		return;

	// if (sched_feat(NEXT_BUDDY) && scale && !(wake_flags & WF_FORK))
	// 	set_next_buddy(pse);

	/*
	 * We can come here with TIF_NEED_RESCHED already set from new task
	 * wake up path.
	 */
	if (test_tsk_need_resched(curr))
		return;

	/*
	 * Batch and idle tasks do not preempt (their preemption is driven by
	 * the tick):
	 */
	if (p->policy != SCHED_NORMAL)
		return;

	/* Idle tasks are by definition preempted by everybody. */
	if (curr->policy == SCHED_IDLE) {
		resched_task(curr);
		return;
	}

	// if ((sched_feat(WAKEUP_SYNC) && sync) ||
	//     (sched_feat(WAKEUP_OVERLAP) &&
	//      (se->avg_overlap < sysctl_sched_migration_cost &&
	//       pse->avg_overlap < sysctl_sched_migration_cost))) {
	// 	resched_task(curr);
	// 	return;
	// }

	// if (sched_feat(WAKEUP_RUNNING)) {
	// 	if (pse->avg_running < se->avg_running) {
	// 		set_next_buddy(pse);
	// 		resched_task(curr);
	// 		return;
	// 	}
	// }

	// if (!sched_feat(WAKEUP_PREEMPT))
	// 	return;

	// find_matching_se(&se, &pse);

	// BUG_ON(!pse);

	// if (wakeup_preempt_entity(se, pse) == 1) {
	// 	resched_task(curr);
	// 	/*
	// 	 * Only set the backward buddy when the current task is still
	// 	 * on the rq. This can happen when a wakeup gets interleaved
	// 	 * with schedule on the ->pre_schedule() or idle_balance()
	// 	 * point, either of which can * drop the rq lock.
	// 	 *
	// 	 * Also, during early boot the idle thread is in the fair class,
	// 	 * for obvious reasons its a bad idea to schedule back to it.
	// 	 */
	// 	if (unlikely(!se->on_rq || curr == rq->idle))
	// 		return;
	// 	if (sched_feat(LAST_BUDDY) && scale && entity_is_task(se))
	// 		set_last_buddy(se);
	// }
}

static void account_entity_enqueue(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
	update_load_add(&cfs_rq->load, se->load.weight);
	if (!parent_entity(se))
		inc_cpu_load(rq_of(cfs_rq), se->load.weight);
	if (entity_is_task(se)) {
		list_add(&se->group_node, &cfs_rq->tasks);
	}
	cfs_rq->nr_running++;
	se->on_rq = 1;
}

static u64 __sched_period(unsigned long nr_running)
{
	u64 period = sysctl_sched_latency;
	unsigned long nr_latency = sched_nr_latency;

	if (nr_running > nr_latency) {
		period = sysctl_sched_min_granularity;
		period *= nr_running;
	}

	return period;
}

static u64 sched_slice(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
	u64 slice = __sched_period(cfs_rq->nr_running + !se->on_rq);

	for_each_sched_entity(se) {
		struct load_weight *load;
		struct load_weight lw;

		cfs_rq = cfs_rq_of(se);
		load = &cfs_rq->load;

		if (!se->on_rq) {
			lw = cfs_rq->load;

			update_load_add(&lw, se->load.weight);
			load = &lw;
		}
		slice = calc_delta_mine(slice, se->load.weight, load);
	}
	return slice;
}

static u64 sched_vslice(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
	return calc_delta_fair(sched_slice(cfs_rq, se), se);
}

static void place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int initial)
{
#if 0
	u64 vruntime = cfs_rq->min_vruntime;

	/*
	 * The 'current' period is already promised to the current tasks,
	 * however the extra weight of the new task will slow them down a
	 * little, place the new task so that it fits in the slot that
	 * stays open at the end.
	 */
	if (initial && sched_feat(START_DEBIT))
		vruntime += sched_vslice(cfs_rq, se);

	/* sleeps up to a single latency don't count. */
	if (!initial && sched_feat(FAIR_SLEEPERS)) {
		unsigned long thresh = sysctl_sched_latency;

		/*
		 * Convert the sleeper threshold into virtual time.
		 * SCHED_IDLE is a special sub-class.  We care about
		 * fairness only relative to other SCHED_IDLE tasks,
		 * all of which have the same weight.
		 */
		if (sched_feat(NORMALIZED_SLEEPER) && (!entity_is_task(se) ||
				 task_of(se)->policy != SCHED_IDLE))
			thresh = calc_delta_fair(thresh, se);

		/*
		 * Halve their sleep time's effect, to allow
		 * for a gentler effect of sleepers:
		 */
		if (sched_feat(GENTLE_FAIR_SLEEPERS))
			thresh >>= 1;

		vruntime -= thresh;
	}

	/* ensure we never gain time by being placed backwards. */
	vruntime = max_vruntime(se->vruntime, vruntime);

	se->vruntime = vruntime;
#endif

	u64 vruntime;

	vruntime = cfs_rq->min_vruntime; 

	if (initial) 
		// vruntime += sched_vslice_add(cfs_rq, se); 
		vruntime += sched_vslice(cfs_rq, se);

	if (!initial) { 
		vruntime -= sysctl_sched_latency; 
		vruntime = max_vruntime(se->vruntime, vruntime); 
	} 

	se->vruntime = vruntime;
}

static inline s64 entity_key(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
	return se->vruntime - cfs_rq->min_vruntime;
}

static void __enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
	struct rb_node **link = &cfs_rq->tasks_timeline.rb_node;
	struct rb_node *parent = NULL;
	struct sched_entity *entry;
	s64 key = entity_key(cfs_rq, se);
	int leftmost = 1;

	// printf("this is %s(): %d\r\n", __func__, __LINE__);

	/*
	 * Find the right place in the rbtree:
	 */
	while (*link) {
		parent = *link;
		entry = rb_entry(parent, struct sched_entity, run_node);
		/*
		 * We dont care about collisions. Nodes with
		 * the same key stay together.
		 */
		if (key < entity_key(cfs_rq, entry)) {
			link = &parent->rb_left;
		} else {
			link = &parent->rb_right;
			leftmost = 0;
		}
	}

	/*
	 * Maintain a cache of leftmost tree entries (it is frequently
	 * used):
	 */
	if (leftmost) {
		// printf("this is %s(): %d\r\n", __func__, __LINE__);
		cfs_rq->rb_leftmost = &se->run_node;
	}

	rb_link_node(&se->run_node, parent, link);
	rb_insert_color(&se->run_node, &cfs_rq->tasks_timeline);
}

static void enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int wakeup)
{
	/*
	 * Update run-time statistics of the 'current'.
	 */
	update_curr(cfs_rq);
	account_entity_enqueue(cfs_rq, se);

	if (wakeup) {
		place_entity(cfs_rq, se, 0);
		// enqueue_sleeper(cfs_rq, se);
	}

	// update_stats_enqueue(cfs_rq, se);
	// check_spread(cfs_rq, se);
	if (se != cfs_rq->curr)
		__enqueue_entity(cfs_rq, se);
}

static void enqueue_task_fair(struct rq *rq, struct task_struct *p, int wakeup)
{
	struct cfs_rq *cfs_rq;
	struct sched_entity *se = &p->se;

	for_each_sched_entity(se) {
		if (se->on_rq)
			break;
		cfs_rq = cfs_rq_of(se);
		enqueue_entity(cfs_rq, se, wakeup);
		wakeup = 1;
	}
}

static void account_entity_dequeue(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
	update_load_sub(&cfs_rq->load, se->load.weight);
	if (!parent_entity(se))
		dec_cpu_load(rq_of(cfs_rq), se->load.weight);
	if (entity_is_task(se)) {
		list_del_init(&se->group_node);
	}
	cfs_rq->nr_running--;
	se->on_rq = 0;
}

static inline int entity_before(struct sched_entity *a,
				struct sched_entity *b)
{
	return (s64)(a->vruntime - b->vruntime) < 0;
}

static void dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int sleep)
{
	/*
	 * Update run-time statistics of the 'current'.
	 */
	update_curr(cfs_rq);

	if (se != cfs_rq->curr)
		__dequeue_entity(cfs_rq, se);
	account_entity_dequeue(cfs_rq, se);
	update_min_vruntime(cfs_rq);
}

static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int sleep)
{
	struct cfs_rq *cfs_rq;
	struct sched_entity *se = &p->se;

	for_each_sched_entity(se) {
		cfs_rq = cfs_rq_of(se);
		dequeue_entity(cfs_rq, se, sleep);
		/* Don't dequeue parent if it has other entities besides us */
		if (cfs_rq->load.weight)
			break;
		sleep = 1;
	}

	// hrtick_update(rq);
}

static void put_prev_entity(struct cfs_rq *cfs_rq, struct sched_entity *prev)
{
	/*
	 * If still on the runqueue then deactivate_task()
	 * was not called and update_curr() has to be done:
	 */
	if (prev->on_rq)
		update_curr(cfs_rq);

	// check_spread(cfs_rq, prev);
	if (prev->on_rq) {
		// update_stats_wait_start(cfs_rq, prev);
		/* Put 'current' back into the tree. */
		__enqueue_entity(cfs_rq, prev);
	}
	cfs_rq->curr = NULL;
}

static void put_prev_task_fair(struct rq *rq, struct task_struct *prev)
{
	struct sched_entity *se = &prev->se;
	struct cfs_rq *cfs_rq;

	for_each_sched_entity(se) {
		cfs_rq = cfs_rq_of(se);
		put_prev_entity(cfs_rq, se);
	}
}

/*
 * Preempt the current task with a newly woken task if needed:
 */
static void
check_preempt_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr)
{
	unsigned long ideal_runtime, delta_exec;

	ideal_runtime = sched_slice(cfs_rq, curr);
	delta_exec = curr->sum_exec_runtime - curr->prev_sum_exec_runtime;
	if (delta_exec > ideal_runtime) {
		resched_task(rq_of(cfs_rq)->curr);
		/*
		 * The current task ran long enough, ensure it doesn't get
		 * re-elected due to buddy favours.
		 */
		clear_buddies(cfs_rq, curr);
		return;
	}

	/*
	 * Ensure that a task that missed wakeup preemption by a
	 * narrow margin doesn't have to wait for a full slice.
	 * This also mitigates buddy induced latencies under load.
	 */
	// if (!sched_feat(WAKEUP_PREEMPT))
	// 	return;

	if (delta_exec < sysctl_sched_min_granularity)
		return;

	if (cfs_rq->nr_running > 1) {
		struct sched_entity *se = __pick_next_entity(cfs_rq);
		s64 delta = curr->vruntime - se->vruntime;

		if (delta > ideal_runtime)
			resched_task(rq_of(cfs_rq)->curr);
	}
}

static void entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued)
{
	/*
	 * Update run-time statistics of the 'current'.
	 */
	update_curr(cfs_rq);

	// if (cfs_rq->nr_running > 1 || !sched_feat(WAKEUP_PREEMPT))
	if (cfs_rq->nr_running > 1)
		check_preempt_tick(cfs_rq, curr);
}

static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued)
{
	struct cfs_rq *cfs_rq;
	struct sched_entity *se = &curr->se;

	for_each_sched_entity(se) {
		cfs_rq = cfs_rq_of(se);
		entity_tick(cfs_rq, se, queued);
	}
}

unsigned int sysctl_sched_child_runs_first;

static void task_new_fair(struct rq *rq, struct task_struct *p)
{
	struct cfs_rq *cfs_rq = task_cfs_rq(p);
	struct sched_entity *se = &p->se, *curr = cfs_rq->curr;
	int this_cpu = smp_processor_id();
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	// sched_info_queued(p);

	update_curr(cfs_rq);
	if (curr)
		se->vruntime = curr->vruntime;
	place_entity(cfs_rq, se, 1);

	/* 'curr' will be NULL if the child belongs to a different group */
	if (sysctl_sched_child_runs_first && this_cpu == task_cpu(p) &&
			curr && entity_before(curr, se)) {
		/*
		 * Upon rescheduling, sched_class::put_prev_task() will place
		 * 'current' within the tree based on its new key value.
		 */
		swap(curr->vruntime, se->vruntime);
		resched_task(rq->curr);
	}

	enqueue_task_fair(rq, p, 0);
}

static const struct sched_class fair_sched_class = {
// const struct sched_class fair_sched_class = {
	// .next			= &idle_sched_class,
	.enqueue_task		= enqueue_task_fair,
	.dequeue_task		= dequeue_task_fair,
	// .yield_task		= yield_task_fair,

	.check_preempt_curr	= check_preempt_wakeup,

	.pick_next_task		= pick_next_task_fair,
	.put_prev_task		= put_prev_task_fair,

	// .set_curr_task          = set_curr_task_fair,
	.task_tick		= task_tick_fair,
	.task_new		= task_new_fair,
};